#include <sys/types.h>   // Definicao de tipos
#include <sys/socket.h>  // Biblioteca de estrutura de sockets
#include <netinet/in.h>  // Define os padroes de protocolo IP
#include <arpa/inet.h>   // Converte enderecos hosts
#include <unistd.h> // Define constantes POSIX
#include <errno.h>  // Utilizado para capturar mensagens de erro
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <time.h>

#define SIZE 500

int* quicksort(int* v, int p, int r) {
    int j;

    if (p < r) {
        j = partition(v, p, r);
        quicksort(v, p, j-1);
        quicksort(v, j+1, r);
    }
    return v;
}

int partition(int* v, int p, int r) {
    int c = v[p], i = p+1, j = r, t;
    while (i <= j) {
        if (v[i] <= c) ++i;
        else if (c < v[j]) --j; 
        else {
            t = v[i], v[i] = v[j], v[j] = t;
            ++i;
            --j;
        }
    }

    v[p] = v[j], v[j] = c;
    return j; 
}

double getTime() { 
	struct timeval time;
	gettimeofday(&time, 0); // int gettimeofday(struct timeval *, void *);
	return time.tv_sec + time.tv_usec / 1e6; // tv_sec = segundos
											 // tv_usec = microsegundos (por isso a divisao por 1 milhao)
}

double conecta_clientes(int sock, struct sockaddr_in server_addr, int* vector_ordenado, int nservers, char** servers) {
	int i;
	double time;
	
	struct hostent *host;

	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		perror("Erro de Socket");
		exit(1);
	}

	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(6000);
	bzero(&(server_addr.sin_zero), 8);

	time = getTime();

	for(i = 1; i <= nservers; i++) {
		host = gethostbyname(servers[i]);
		server_addr.sin_addr = *((struct in_addr *)host->h_addr);
		if (connect(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) {
			perror("Erro de conexao");
			exit(1);
		}
		send(sock, vector_ordenado, 2 * SIZE * sizeof(int), 0);
	}
	
	return getTime() - time;
}

int main(int argc, char *argv[]) {
	
	int sock, connected, bytes_recv, i, true = 1;
	char send_data[4096] , recv_data[4096];
	int *vector, *vector_ordenado, recv_vector[SIZE];
	struct sockaddr_in server_addr, client_addr;
	int sin_size, nclients;
	double time_ord, time_send;
	
	if(argc != 9) {
        printf("Usage: %s <hostname> * 8\n", argv[0]);
        return 1;
    }

	vector = (int *)malloc(sizeof(int)*(SIZE*2));
    vector_ordenado = (int *)malloc(sizeof(int)*(SIZE*2));

	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		perror("Erro no Socket");
		exit(1);
	}

	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &true, sizeof(int)) == -1) {
		perror("Erro Setsockopt");
		exit(1);
	}

	// Configura o endereco de destino
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(9000);
	server_addr.sin_addr.s_addr = INADDR_ANY;
	bzero(&(server_addr.sin_zero), 8);


	if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) {
		perror("Nao foi possivel realizar o bind");
		exit(1);
	}

	if (listen(sock, 10) == -1) {
		perror("Erro de Listen");
		exit(1);
	}
	
	// Loop para receber varias solicitacoes
	for(nclients = 0; nclients < 2; nclients++) {

		// Variavel para armazenar o tamanho de endereco do cliente conectado
		sin_size = sizeof(struct sockaddr_in);
		
		connected = accept(sock, (struct sockaddr *)&client_addr, &sin_size);
		//printf("Conexão aceita de (%s, %d)\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

		// Funcao recv (int socket, void *buffer, size_t size, int flags)        
		bytes_recv = recv(connected, recv_vector, sizeof(recv_vector), 0);

		for(i = 0; i < SIZE; i++) {
			vector[i + nclients * SIZE] = recv_vector[i];
		}
	}
	
	for(i = 0; i < SIZE * 2; i++) {
		printf("%d ", vector[i]);
	}
	printf("\n\n");
	time_ord = getTime();
	vector_ordenado = quicksort(vector, 0, SIZE * 2 - 1);
	time_ord = getTime() - time_ord;
	for(i = 0; i < SIZE * 2; i++) {
		printf("%d ", vector_ordenado[i]);
	}
	
	printf("\nTempo de ordenacao %f", time_ord);

	time_send = conecta_clientes(sock, server_addr, vector_ordenado, argc, argv);
	
	printf("\nTempo de envio %f", time_send);

	close(sock);
	
	return 0;
}
